开始前
Promise
的实现原理已经在 Promise 规范解读及实现细节 (一) 中说的很清楚了,这里将详细分析 Promises/A+规范 中的Promise
解析过程,最后会实现一个 Promise
并提供用于测试的代码
then 方法分析
promise.then(fn1).then(fn2).then(fn3)
这里 promise.then()
的调用会产生一个新的promise(不同实例)
对于 then(fn)
上一级 promise
的终值会作为 fn
的参数被传入
对于 then(fn)
如果 then
返回一个 promise1
,fn
返回一个 promise2
那么 promise1
的状态和值由 promise2
决定
对于 then(fn)
如果 then
返回一个 promise1
,fn
返回一个 promise2
我们想依赖于promise2
的调用会被添加到 promise1
中
如何让 promise2
决定 promise1
的结果
如果promise2
有 then
方 var promise2Then = promise2.then
,我们这样promise2Then.bind(promise1,resolve,reject)
promise
的状态和终值都是通过 当前 promise
的 resolve
和 reject
来改变的,所以只需要将 promise1
的这两个函数通过promise2
的 then
方法添加的 promise2
的执行队列中就可以达到想要的效果
Promise 解析过程
对于 promise.then(fn)
,then
方法返回 promise1
,fn
方法返回 x
,fn
接收到的参数为 y
,这时需要对 x
和 y
分别运行Promise
解析过程
x: [[Resolve]](promise1, x)
y: [[Resolve]](promise1, y)
x
的解析过程处理的是 回掉中返回 promise
的情况y
的解析过程处理的是 向当前 promise
传递处理结果的那个 promise
的终值是 promise
的情况
由此可见 promise
的解析过程是递归的,递归的终点是 x
,y
不是promise
,在是对象或者函数的情形下不具备 then
方法
代码结构
(function(window) {
var PENDING = 0; //PENDING 状态
var RESOLVED = 1; //RESOLVED 状态
var REJECTED = 2; //REJECTED 状态
function IPromise(fn) {
if (!(this instanceof IPromise)) return new IPromise(fn); //确保 通过 new IPromise() 和 IPromise() 都能正确创建对象
var state = PENDING; //promise 状态
var value = null; //终值
var callback = []; //回掉函数队列,在一种是两个队列,这里是一个,所以存放的是对象
function reject(reason) {} //reject 方法
function resolve(result) {} //和(一)中的对比 这里多了 执行 Promise 解析过程 的功能
function handle(handler) {} //添加或执行队 callback 中的调用
/**
*@param onFulfilled 通过 then 方法添加的 onFulfilled
*@param onRejected 通过 then 方法添加的 onRejected
*
*@func 包装用户添加的回调
*因为这里只有一个回掉队列所以需要用 candy(糖果) 包装成{onFulfilled:onFulfilled,onRejected:onRejected}
*
*@func 延迟调用handle
*在 Promise 规范解读及实现细节 (一) 中说 Promise(浏览器实现) 会被加入到microtask,由于浏览器没有提供除Promise
*之外microtask的接口,所以 我们要用 setTimeout 来延迟调用并添加到 macrotask
*
*/
function candy(onFulfilled, onRejected) {}
function getThen(value) {} //判断 value 是否有 then 方法如果有则获取
this.then = function(onFulfilled, onRejected) {} //暴露的 then 方法
doResolve(fn, resolve, reject); //执行 fn
window.IPromise = IPromise; //将 IPromise 添加到浏览器的 window 上
}
/**
*Promise 解析过程
*/
function doResolve(fn, resolvePromise, rejectPromise) {} //静态私有方法,解析并执行promise(解析并执行fn和promise的处理结果)
})(window);
以上通过 js
自执行函数将变量和函数限制在了作用域中,在全局的 window
上只暴露一个构造函数 IPromise
保证了全局不被污染
具体代码及解释(请在浏览器中运行)
/**
*@author ivenj
*@date 2016-12-06
*@version 1.0
*/
(function(window) {
var PENDING = 0;
var RESOLVED = 1;
var REJECTED = 2;
function IPromise(fn) {
if (!(this instanceof IPromise)) return new IPromise(fn);
var state = PENDING;
var value = null;
var callback = [];
function reject(reason) {
state = REJECTED;
value = reason;
callback.forEach(handle);
callback = null;
}
/**
* 这里新增的内容是 满足Promise解析过程时 resolve和doResolve相互调用形成递归
**/
function resolve(result) {
try {
var then = getThen(result);
if (then) {
doResolve(then.bind(result), resolve, reject); //aa
return; //这里如果 resule 是有 then 方法则执行 doResolve 并返回不执行后续代码
}
//只有 result 不满足 解析过程时执行,即递归终点
state = RESOLVED;
value = result;
callback.forEach(handle);
callback = null;
} catch (e) {
reject(e);
}
}
function handle(handler) {
if (state === PENDING) {
callback.push(handler);
} else {
if (state === RESOLVED && typeof handler.onFulfilled === 'function') {
handler.onFulfilled(value);
}
if (state === REJECTED && typeof handler.onRejected === 'function') {
handler.onRejected(value);
}
}
}
function candy(onFulfilled, onRejected) {
setTimeout(function() {
handle({
onFulfilled: onFulfilled,
onRejected: onRejected
});
}, 0);
}
function getThen(value) {
var type = typeof value;
if (value && (type === 'object' || type === 'function')) {
try{
var then = value.then;
}catch(e){
reject(e);
}
if (typeof then === 'function') {
return then;
}
}
return null;
}
this.then = function(onFulfilled, onRejected) {
var self = this;
return new IPromise(function(resolve, reject) {
candy(function(x) {
if (typeof onFulfilled === 'function') {
try {
resolve(onFulfilled(x)); //cc 运行 [[Resolve]](promise, x)
} catch (e) {
reject(e);
}
} else {
resolve(x);
}
}, function(error) {
if (typeof onRejected === 'function') {
try {
resolve(onRejected(error));
} catch (e) {
reject(e);
}
} else {
reject(error);
}
});
});
};
doResolve(fn, resolve, reject);
}
/**
*Promise 解析过程
*/
function doResolve(fn, resolvePromise, rejectPromise) {
var done = false; //用于保证只调用一次
try {
fn(function(y) {
if (done) return;
done = true;
resolvePromise(y); //bb 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
}, function(reason) {
if (done) return;
done = true;
rejectPromise(reason);
});
} catch (e) {
if (done) return;
done = true;
rejectPromise(e);
}
}
window.IPromise = IPromise;
})(window);
这里是用于测试的代码 读者将以上代码和以下代码粘贴到浏览器去运行 一秒后会打印 {url: "http://ivenj_", value: 10}
function post(url, callback) {
setTimeout(function() {
var data = { //模拟异步处理结果
url:url,
value:10
};
callback(data);
}, 1000);
}
var promise = IPromise(function(resolve, reject){
post('http://ivenj_', function(data){
resolve(data);
});
});
promise.then(function(data){
console.log(data);
});
Promise
实现最核心的内容是代码中的 //aa
//bb
//cc
读者需要着重体会这三处
Promise 到此已经结束
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。